<?php
/* SVN FILE: $Id$ */
/**
 * Component Paginator class.
 *
 * PHP versions 4 and 5
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 * @package       mtc
 * @subpackage    mtc.cake.component
 * @version       $Revision$
 * @modifiedby    $LastChangedBy$
 * @lastmodified  $Date$
 * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
 */
?>
<?php
/**
 * Include files
 */

/**
 * PaginatorComponent
 *
 * Classe responsável por centralizar as funções relativas ao controle de paginação.
 *
 * @package       mtc
 * @subpackage    mtc.cake.component
 *
 */
class PaginatorComponent extends Object {

   /**
     * Components utilizados
     * @var array components
     */
    var $components = array('RequestHandler');

    /**
     * controller que utilizará a paginação.
     * @var Controller controller
     */
    var $controller = null;

    /**
     * DomId da div que será atualizada, quando se usa ajax no paginate
     * @var string updateId
     * @access private
     */
    var $updateId = 'content';

    //mensagens

    /**
     * Mensagem que indica a falta do atributo $extPaginator no controller.
     */
    const NO_EXT_PAGINATE_ERROR = 'can\'t find the attribute extPaginate in controller %1$sController';

    /**
     * Mensagem que indica quando se passa um tipo de filtro desconhecido.
     */
    const NO_TYPE_FILTER_ERROR = 'can\'t find type %1$s';

    /**
     * Obtem referência para atributos do @param $controller serão lidas e/ou modificadas.
     * @param Controller $controller
     * @param array $settings
     * @access public
     */
    function initialize(&$controller, $settings = array()) {
        $this->controller = $controller;
        if(!isset($controller->extPaginate)){
            $this->error("initialize",self::NO_EXT_PAGINATE_ERROR, $controller->name);
            return;
        }        
        //usado para se utilizar ajax na paginação
        if(isset($settings['update'])) {
            $this->update = $settings['update'];
        }
    }
    
    /**
     * Adiciona os parametros nomeados no array de dados. .
     * Ou seja, faz com que os parametros passados pela paginação
     * (pela url) sejam carregados como dados.
     * @param Controller $controller
     * @access public
     */
    function startup(&$controller) {
        $action = $controller->params['action'];

        if(!isset($controller->extPaginate) ||
           !isset($controller->extPaginate[$action])) {
            return;
        }               

        $args = $controller->passedArgs;
        $filters = $controller->extPaginate[$action];

        foreach($filters as $field => $typeFilter) {

            list($model,$field) = $this->explodeField($field);

            switch($typeFilter){
                case 'filter':
                case 'filterAll':
                case 'select':
                case 'urlParam':
                    if(isset($args["{$model}.{$field}"])) {
                        $controller->data[$model][$field] = $args["{$model}.{$field}"];
                    }
                    break;
                case 'param':
                    // 'param' não é colocado na variável data
                    break;
                default:
                    $this->error('startup', self::NO_TYPE_FILTER_ERROR, $typeFilter);
            }
        }
    }

    /**
     * Como $controller->paginate(...), porém adiciona as condições dos filtros 
     * na pesquisa da paginação e na url que será montada.
     * @param mixed $object
     * @param mixed $scope
     * @param array $whitelist
     * @return array Model query results
     * @access public
     */
    function paginate($object = null, $scope = array(), $whitelist = array()) {
        if(!empty($this->controller->data)) {

            list($url,$conditions) = $this->extractData();

            if(!empty($conditions)) {
                if(isset($this->controller->paginate['conditions'])) {
                    $conditions = array_merge((array)$conditions, (array)$this->controller->paginate['conditions']);
                }
                $this->controller->paginate['conditions'] = $conditions;
            }
        }

        $result = $this->controller->paginate($object,$scope,$whitelist);
        
        if(!empty($url)) {
                // adiciona os parametros que serão acrescentados na url da paginação.
                $this->controller->params['search'] = $url;
        }

        //usado para ativar o uso de ajax na paginação
        if(isset($this->RequestHandler)
                && $this->RequestHandler->isAjax()) {
                $this->controller->params['update'] = $this->updateId;
        }

        return $result;
    }
     
    /**
     * Constrói o array de condições (conditions) que será utilizado para fazer a paginação
     *  e array de url que será adicionado nas 'links' da paginação.
     * Isso é feito atraves do array de dados (controller->data) e dos filtros usados (controller->extPaginate)
     * @return array array(url,conditions)
     * @access private
     */
    function extractData(){
        if(!isset($this->controller->extPaginate)) {
            return;
        }
        $conditions = array();
        $url = array();
        $action = $this->controller->params['action'];
        if(isset($this->controller->extPaginate[$action])) {
            $data = $this->controller->data;
            $filters = $this->controller->extPaginate[$action];

            foreach($filters as $field => $typeField) {

                list($model,$field) = $this->explodeField($field);

                if(isset($data[$model][$field])) {
                    switch($typeField){
                        case 'filter':
                            $value = $data[ $model ] [ $field ];
                            $conditions["{$model}.{$field} LIKE"] =  $value  . '%';
                            $url["{$model}.{$field}"] = $value;
                            break;
                         case 'filterAll':
                            $value = $data[ $model ] [ $field ];
                            $conditions["{$model}.{$field} LIKE"] =  '%'. $value  . '%';
                            $url["{$model}.{$field}"] = $value;
                            break;
                        case 'select':
                            $value = $data[ $model ] [ $field ];
                            $conditions["{$model}.{$field}"] =  $value;
                            $url["{$model}.{$field}"] = $value;
                            break;
                        case 'urlParam':
                            $value = $data[ $model ] [ $field ];
                            $url["{$model}.{$field}"] = $value;
                            break;
                        case 'param':
                            $value = $data[ $model ] [ $field ];
                            $conditions["{$model}.{$field}"] =  $value;
                            $url[] = $value;
                            break;
                        default:
                            $this->error('extractData',self::NO_TYPE_FILTER_ERROR,$typeField);
                            break;
                    }
                }
            }
        }
        return array($url,$conditions);
    }

    /**
     * Dado uma string $field do tipo "model.field" retorna um array com ("model","field")
     * caso nao seja passado o model então "model" será o model padrão do controller.
     * @param string $field
     * @return array array(modelName,FieldName)
     * @access private
     */
    function explodeField($field) {
        if(strpos($field, '.') !== false) {
            return explode('.', $field);
        } else {
            return array($this->controller->modelClass, $field);
        }
    }

    /**
     * Lançará um erro na tela, exibindo qual método o lançou e uma mensagem.
     * @param string $method nome do método em que o erro foi dectado.
     * @param string $errorMessage mensagem do erro que será exibida
     * @param mixed $argsMessage caso a mensagem ($errorMessage) seja parametrizada poderá estes serão os argumentos
     * @param <type> $errorType tipo do erro: error/warning/notice
     */
    function error($method, $errorMessage, $argsMessage = null,$errorType = E_USER_WARNING ){
        trigger_error(sprintf("PaginatorComponent::{$method}() - {$errorMessage}",$argsMessage), $errorType);
    }

}
?>
